﻿using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using BeanboxSoftware.BeanProxy;
using System.Reflection;
using System.IO;
using System.IO.Packaging;
using System.Drawing;
using BeanboxSoftware.BeanProxy.Internals;

namespace BeanboxSoftware.BeanProxyTest
{
  [TestClass]
  public class ProxyBuilderTest
  {
    [TestMethod]
    public void IsInternalTest()
    {
      var builder = new Builder<ProxyBuilderTest>();
      var result = builder.IsInternal(((Action)(this.InternalMethod)).Method);
      Assert.IsTrue(result, "is internal result");
      Assert.IsFalse(builder.IsInternal((ConstructorInfo)null), "null");

      var prox = ProxyManager.CreateProxy<Builder<ProxyBuilderTest>>();

      var ctor = prox.GetType().GetConstructor(new Type[] { typeof(CtorConfig) });

      ProxyManager.GetFunc<ConstructorInfo, bool>(prox.IsInternal).IsReal = true;
      ProxyManager.GetFunc<Assembly, bool>(prox.IsExternal).IsReal = true;

      ProxyManager.GetFunc<Type, bool>(prox.IsInternal).Return = true;
      //because the reflected type is internal
      Assert.IsTrue(prox.IsInternal(ctor), "reflected type");

      //skip once to get to the parameters
      bool firstCheck = true;
      ProxyManager.GetFunc<Type, bool>(prox.IsInternal).Callback = (t) =>
      {
        if (firstCheck)
        {
          firstCheck = false;
          return false;
        }

        return true;
      };

      //because a param is internal
      Assert.IsTrue(prox.IsInternal(ctor), "param is internal");
      Assert.IsFalse(firstCheck, "first check");
    }

    [TestMethod]
    public void IsVirtualTest()
    {
      var builder = new Builder<ProxyBuilderTest>();
      var result = builder.IsVirtual(((Action)(this.InternalMethod)).Method);
      Assert.IsFalse(result, "is virtual result for internal method");
    }

    internal virtual void InternalMethod()
    {
    }

    [TestMethod]
    public void DifficultParametersTest()
    {
      var proxy = ProxyManager.CreateProxy<DifficultParameter>();

      var weirdFooConfig =
          ProxyManager.GetFunc<int?, int[], List<int>, List<int?>[], int[], Func<Func<int>>>(proxy.WeirdFoo);

      weirdFooConfig.IsReal = true;

      TestUtils.AssertExceptionThrown<NotImplementedException>(() =>
      {
        proxy.WeirdFoo(null, null, null, null);
      });
    }

    public delegate int FooType(ref int rint, out int oint);
    [TestMethod]
    public void RefOutTest()
    {
      var proxy = ProxyManager.CreateProxy<DifficultParameter>();

      int fakeOut = 4;
      int fakeReturn = 7;

      var fooConfig = ProxyManager.GetMethod<FooType>(proxy.Foo);
      fooConfig.IsReal = false;
      fooConfig.Access.Add(va => 
      {
        va[0] = (int)va[0] + 1;
        va[1] = fakeOut;
        return fakeReturn;
      });

      int rint = 10;
      int oint;

      var result = proxy.Foo(ref rint, out oint);

      Assert.AreEqual(11, rint);
      Assert.AreEqual(fakeReturn, result, "return");
      Assert.AreEqual(fakeOut, oint, "oint");
    }

    [TestMethod]
    public void AgeTest()
    {
      var proxy = ProxyManager.CreateProxy<DifficultParameter>();

      var getAgeConfig = ProxyManager.GetProp<int>(proxy, () => { return proxy.Age; });
      var setAgeConfig = ProxyManager.GetProp<int>(proxy, (v) => { proxy.Age = v; });

      bool ageSet = false;
      getAgeConfig.Return = 15;
      setAgeConfig.Callback = (v) => { ageSet = true; };

      proxy.Age = 77;
      Assert.AreEqual(15, proxy.Age, "get_Age");
      Assert.IsTrue(ageSet, "set_Age");
    }

    [TestMethod]
    public void PackageTest()
    {
      var proxy = ProxyManager.CreateProxy<PackageClass>();

      ProxyManager.GetFunc<string, byte[], Stream>(proxy.BuildDocument).IsReal = true;

      string text = "text";
      byte[] imageData = new byte[] { 0x10 };

      var package = ProxyManager.CreateProxy<Package>(FileAccess.ReadWrite);

      var image = new Bitmap(1, 1);

      ProxyManager.GetFunc<Package>(proxy.BuildEmptyPackage).Return = package;
      ProxyManager.GetFunc<byte[], Image>(proxy.CreateImage).Callback = (data) =>
      {
        Assert.AreEqual(imageData, data, "image data");
        return image;
      };

      ProxyManager.GetAction<Package, string>(proxy.WriteText).Callback = (pkg, txt) =>
      {
        Assert.AreEqual(package, pkg, "package");
        Assert.AreEqual(text, txt, "text");
      };

      ProxyManager.GetAction<Package, Image>(proxy.WriteImage).Callback = (pkg, img) =>
      {
        Assert.AreEqual(package, pkg, "package");
        Assert.AreEqual(image, img, "image");
      };

      var stream = ProxyManager.CreateProxy<Stream>();

      ProxyManager.GetFunc<Package, Stream>(proxy.GetPackageStream).Return = stream;

      var result = proxy.BuildDocument(text, imageData);

      Assert.AreEqual(stream, result, "result");
    }

    [TestMethod]
    public void CtorStuffTest()
    {
      TestUtils.AssertExceptionThrown<ArgumentException>(() =>
      {
        ProxyManager.CreateProxy<Image>();
      });
    }
  }

  public class DifficultParameter
  {
    public virtual int Age { get; set; }

    public virtual Func<Func<int>> WeirdFoo(int? nullable, int[] array, List<int> templated, List<int?>[] all, params int[] parms)
    {
      throw new NotImplementedException();
    }

    public virtual int Foo(ref int rint, out int oint)
    {
      oint = rint;
      return rint;
    }
  }

  public class PackageClass
  {
    public virtual Stream BuildDocument(string text, byte[] imageData)
    {
      var package = BuildEmptyPackage();
      var image = CreateImage(imageData);

      WriteText(package, text);
      WriteImage(package, image);

      return GetPackageStream(package);
    }

    public virtual Package BuildEmptyPackage()
    {
      throw new NotImplementedException();
    }

    public virtual Image CreateImage(byte[] imageData)
    {
      throw new NotImplementedException();
    }

    public virtual void WriteText(Package pkg, string text)
    {
      throw new NotImplementedException();
    }

    public virtual void WriteImage(Package pkg, Image img)
    {
      throw new NotImplementedException();
    }

    public virtual Stream GetPackageStream(Package pkg)
    {
      throw new NotImplementedException();
    }
  }
}

///
